package com.qsw.datetimepickerlib.DateTimePicker.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.qsw.datetimepickerlib.R;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 日期时间选择器
 */
public class DateTimePicker extends LinearLayout {
    private static final String TAG = "TAG_DateTimePicker";

    private WheelView mYearWheelView;
    private WheelView mMonthWheelView;
    private WheelView mDayWheelView;
    private WheelView mHourWheelView;
    private WheelView mMinuteWheelView;

    private Calendar mMinCalendar;
    private Calendar mCalendar;

    private OnDateTimeChangedListener mOnDateTimeChangedListener;

    private boolean mShowFutureDate = true;
    private boolean mHasInitYear = false;
    private boolean mHasInitMonth = false;
    private boolean mHasInitDay = false;
    private boolean mHasInitHour = false;
    private boolean mHasInitMinute = false;

    public DateTimePicker(Context context) {
        this(context, null);
    }

    public DateTimePicker(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DateTimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mMinCalendar = Calendar.getInstance();
        mMinCalendar.set(1970, 0, 1, 0, 0, 0);
        mMinCalendar.set(Calendar.MILLISECOND, 0);
        mCalendar = Calendar.getInstance();
        initSelf();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!mHasInitYear) {
            setYear(mCalendar.get(Calendar.YEAR));
        }
        if (!mHasInitMonth) {
            setMonth(mCalendar.get(Calendar.MONTH) + 1);
        }
        if (!mHasInitDay) {
            setDay(mCalendar.get(Calendar.DAY_OF_MONTH));
        }
        if (!mHasInitHour) {
            setHour(mCalendar.get(Calendar.HOUR_OF_DAY));
        }
        if (!mHasInitMinute) {
            setMinute(mCalendar.get(Calendar.MINUTE));
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        calibrateMonthData();
        calibrateDayData();
        calibrateHourData();
        calibrateMinuteData();
    }

    /**
     * 添加内部WheelView及初始化WheelView数据
     */
    private void initSelf() {
        removeAllViews();
        setBackgroundColor(getResources().getColor(R.color.dtpllib_color_datatime_picker_bg));

        int lastYear;
        int lastMonth;
        int lastDay;
        int lastHour;
        int lastMinute;
        if (mShowFutureDate) {
            lastYear = mCalendar.get(Calendar.YEAR) + 20;
            lastMonth = 13;
            lastDay = DTPLUtil.getDayNumOfMonth(mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH) + 1) + 1;
            lastHour = 24;
            lastMinute = 60;
        } else {
            lastYear = mCalendar.get(Calendar.YEAR) + 1;
            lastMonth = mCalendar.get(Calendar.MONTH) + 2;
            lastDay = mCalendar.get(Calendar.DAY_OF_MONTH) + 1;
            lastHour = mCalendar.get(Calendar.HOUR_OF_DAY) + 1;
            lastMinute = mCalendar.get(Calendar.MINUTE) + 1;
        }

        LayoutParams wheelViewParams = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
        wheelViewParams.setMargins(10, 0, 10, 0);
        wheelViewParams.weight = 1;

        mYearWheelView = new WheelView(getContext());
        List<String> yearList = new ArrayList<>();
        for (int i = mMinCalendar.get(Calendar.YEAR); i < lastYear; i++) {
            yearList.add("" + i);
        }
        mYearWheelView.setData(yearList);
        mYearWheelView.setLayoutParams(wheelViewParams);
        mYearWheelView.setOnItemChangedListener(mOnItemChangedListener);
        addView(mYearWheelView);

        mMonthWheelView = new WheelView(getContext());
        List<String> monthList = new ArrayList<>();
        int minMonth = (
                mCalendar.get(Calendar.YEAR) == mMinCalendar.get(Calendar.YEAR)
        )
                ? mMinCalendar.get(Calendar.MONTH) + 1
                : 1;
        for (int i = minMonth; i < lastMonth; i++) {
            monthList.add("" + i);
        }
        mMonthWheelView.setData(monthList);
        mMonthWheelView.setLayoutParams(wheelViewParams);
        mMonthWheelView.setOnItemChangedListener(mOnItemChangedListener);
        addView(mMonthWheelView);

        mDayWheelView = new WheelView(getContext());
        List<String> dayList = new ArrayList<>();
        int minDay = (
                mCalendar.get(Calendar.YEAR) == mMinCalendar.get(Calendar.YEAR)
                        && mCalendar.get(Calendar.MONTH) == mMinCalendar.get(Calendar.MONTH)
        )
                ? mMinCalendar.get(Calendar.DAY_OF_MONTH)
                : 1;
        for (int i = minDay; i < lastDay; i++) {
            dayList.add("" + i);
        }
        mDayWheelView.setData(dayList);
        mDayWheelView.setLayoutParams(wheelViewParams);
        mDayWheelView.setOnItemChangedListener(mOnItemChangedListener);
        addView(mDayWheelView);

        mHourWheelView = new WheelView(getContext());
        int minHour = (
                mCalendar.get(Calendar.YEAR) == mMinCalendar.get(Calendar.YEAR)
                        && mCalendar.get(Calendar.MONTH) == mMinCalendar.get(Calendar.MONTH)
                        && mCalendar.get(Calendar.DAY_OF_MONTH) == mMinCalendar.get(Calendar.DAY_OF_MONTH)
        )
                ? mMinCalendar.get(Calendar.HOUR_OF_DAY)
                : 0;
        List<String> hourList = new ArrayList<>();
        for (int i = minHour; i < lastHour; i++) {
            hourList.add("" + i);
        }
        mHourWheelView.setData(hourList);
        mHourWheelView.setLayoutParams(wheelViewParams);
        mHourWheelView.setOnItemChangedListener(mOnItemChangedListener);
        addView(mHourWheelView);

        mMinuteWheelView = new WheelView(getContext());
        List<String> minuteList = new ArrayList<>();
        int minMinute = (
                mCalendar.get(Calendar.YEAR) == mMinCalendar.get(Calendar.YEAR)
                        && mCalendar.get(Calendar.MONTH) == mMinCalendar.get(Calendar.MONTH)
                        && mCalendar.get(Calendar.DAY_OF_MONTH) == mMinCalendar.get(Calendar.DAY_OF_MONTH)
                        && mCalendar.get(Calendar.HOUR_OF_DAY) == mMinCalendar.get(Calendar.HOUR_OF_DAY)
        )
                ? mMinCalendar.get(Calendar.MINUTE)
                : 0;
        for (int i = minMinute; i < lastMinute; i++) {
            minuteList.add(((i < 10) ? "0" : "") + i);
        }
        mMinuteWheelView.setData(minuteList);
        mMinuteWheelView.setLayoutParams(wheelViewParams);
        mMinuteWheelView.setOnItemChangedListener(mOnItemChangedListener);
        addView(mMinuteWheelView);
    }

    /**
     * 设置是否显示未来时间，若设置为不显示，则不能选择当前时刻以后的时间
     *
     * @param showFutureDate
     */
    public void setShowFutureDate(boolean showFutureDate) {
        mShowFutureDate = showFutureDate;
        initSelf();
    }

    /**
     * 设置显示的最小时间
     *
     * @param date 最小时间
     */
    public void setMinDate(Date date) {
        mMinCalendar.setTime(date);
        initSelf();
    }

    /**
     * 设置当前年，必须在界面已经显示后调用
     *
     * @param year 年份的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该年份
     */
    public boolean setYear(int year) {
        if (mYearWheelView.setSelected("" + year)) {
            calibrateMonthData();
            calibrateDayData();
            calibrateHourData();
            calibrateMinuteData();
            return true;
        }
        return false;
    }

    /**
     * 设置当前月，必须在界面已经显示后调用
     *
     * @param month 月份的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该月份
     */
    public boolean setMonth(int month) {
        if (mMonthWheelView.setSelected("" + month)) {
            calibrateDayData();
            calibrateHourData();
            calibrateMinuteData();
            return true;
        }
        return false;
    }

    /**
     * 设置当前天，必须在界面已经显示后调用
     *
     * @param day 天的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该天
     */
    public boolean setDay(int day) {
        if (mDayWheelView.setSelected("" + day)) {
            calibrateHourData();
            calibrateMinuteData();
            return true;
        }
        return false;
    }

    /**
     * 设置当前时，必须在界面已经显示后调用
     *
     * @param hour 时的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该时
     */
    public boolean setHour(int hour) {
        if (mHourWheelView.setSelected("" + hour)) {
            calibrateMinuteData();
            return true;
        }
        return false;
    }

    /**
     * 设置当前分，必须在界面已经显示后调用
     *
     * @param minute 分的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该分
     */
    public boolean setMinute(int minute) {
        return mMinuteWheelView.setSelected((minute < 10 ? "0" : "") + minute);
    }

    /**
     * 获取年的数字形式
     *
     * @return 获取失败则返回-1
     */
    public int getYear() {
        if (mYearWheelView.getSelected() == null) {
            return -1;
        }
        return Integer.valueOf(mYearWheelView.getSelected());
    }

    /**
     * 获取月的数字形式
     *
     * @return 获取失败则返回-1
     */
    public int getMonth() {
        if (mMonthWheelView.getSelected() == null) {
            return -1;
        }
        return Integer.valueOf(mMonthWheelView.getSelected());
    }

    /**
     * 获取日的数字形式
     *
     * @return 获取失败则返回-1
     */
    public int getDay() {
        if (mDayWheelView.getSelected() == null) {
            return -1;
        }
        return Integer.valueOf(mDayWheelView.getSelected());
    }

    /**
     * 获取时的数字形式
     *
     * @return 获取失败则返回-1
     */
    public int getHour() {
        if (mHourWheelView.getSelected() == null) {
            return -1;
        }
        return Integer.valueOf(mHourWheelView.getSelected());
    }

    /**
     * 获取分的数字形式
     *
     * @return 获取失败则返回-1
     */
    public int getMinute() {
        if (mMinuteWheelView.getSelected() == null) {
            return -1;
        }
        return Integer.valueOf(mMinuteWheelView.getSelected());
    }

    /**
     * 设置当前选择的年月日时分，必须在界面已经显示后调用
     *
     * @param year   年的数字形式
     * @param month  月的数字形式
     * @param day    日的数字形式
     * @param hour   时的数字形式
     * @param minute 分的数字形式
     * @return true：设置成功；false：设置失败，列表中没有该时间
     */
    public boolean setDateTime(int year, int month, int day, int hour, int minute) {
        int[] dateTime = getDateTime();
        boolean result;
        result = setYear(year);
        result = setMonth(month) && result;
        result = setDay(day) && result;
        result = setHour(hour) && result;
        result = setMinute(minute) && result;
        if (!result && dateTime[0] != -1) {
            setYear(dateTime[0]);
            setMonth(dateTime[1]);
            setDay(dateTime[2]);
            setHour(dateTime[3]);
            setMinute(dateTime[4]);
        }
        return result;
    }

    /**
     * 初始化当前选择的年月日时分
     *
     * @param year   年的数字形式
     * @param month  月的数字形式
     * @param day    日的数字形式
     * @param hour   时的数字形式
     * @param minute 分的数字形式
     * @return true：设置成功；false：设置失败
     */
    public boolean initDateTime(int year, int month, int day, int hour, int minute) {
        mYearWheelView.setSelected("" + year);
        calibrateMonthData(year);
        mMonthWheelView.setSelected("" + month);
        calibrateDayData(year, month);
        mDayWheelView.setSelected("" + day);
        calibrateHourData(year, month, day);
        mHourWheelView.setSelected("" + hour);
        calibrateMinuteData(year, month, day, hour);
        mMinuteWheelView.setSelected((minute < 10 ? "0" : "") + minute);
        mHasInitYear = true;
        mHasInitMonth = true;
        mHasInitDay = true;
        mHasInitHour = true;
        mHasInitMinute = true;
        return true;
    }

    /**
     * 获取当前选定的日期时间
     *
     * @return 数组长度为5，按索引顺序分别为年、月、日、时、分，若获取失败则元素为-1
     */
    public int[] getDateTime() {
        return new int[]{
                getYear(), getMonth(), getDay(), getHour(), getMinute()
        };
    }

    /**
     * 根据年份变化校准月份数据
     */
    private void calibrateMonthData() {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        int minMonth = (
                mYearWheelView.getSelected() != null
                        && Integer.valueOf(mYearWheelView.getSelected()) == mMinCalendar.get(Calendar.YEAR)
        )
                ? mMinCalendar.get(Calendar.MONTH) + 1
                : 1;
        for (int i = minMonth; i < 13; i++) {
            if (!mShowFutureDate
                    && (mYearWheelView.getSelected() != null)
                    && (Integer.valueOf(mYearWheelView.getSelected()) == mCalendar.get(Calendar.YEAR))
                    && (i > mCalendar.get(Calendar.MONTH) + 1)) {
                break;
            }
            strList.add("" + i);
        }
        selectedPosition = mMonthWheelView.getSelectedPosition();
        mMonthWheelView.setData(strList);
        mMonthWheelView.setSelectedPosition((selectedPosition < strList.size()) ? selectedPosition : (strList.size() - 1));
    }

    /**
     * 根据指定年份校准月份数据，只改变数据不改变当前选择项
     */
    private void calibrateMonthData(int year) {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        int minMonth = (
                year == mMinCalendar.get(Calendar.YEAR)
        )
                ? mMinCalendar.get(Calendar.MONTH) + 1
                : 1;
        for (int i = minMonth; i < 13; i++) {
            if (!mShowFutureDate
                    && (year == mCalendar.get(Calendar.YEAR))
                    && (i > mCalendar.get(Calendar.MONTH) + 1)) {
                break;
            }
            strList.add("" + i);
        }
        mMonthWheelView.setData(strList);
    }

    /**
     * 根据年月变化校准日期数据
     */
    private void calibrateDayData() {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        if ((mYearWheelView.getSelected() == null)
                || (mMonthWheelView.getSelected() == null)) {
            return;
        }
        int minDay = (
                Integer.valueOf(mYearWheelView.getSelected()) == mMinCalendar.get(Calendar.YEAR)
                        && Integer.valueOf(mMonthWheelView.getSelected()) == mMinCalendar.get(Calendar.MONTH) + 1
        )
                ? mMinCalendar.get(Calendar.DAY_OF_MONTH)
                : 1;
        for (int i = minDay; i < DTPLUtil.getDayNumOfMonth(Integer.valueOf(mYearWheelView.getSelected()), Integer.valueOf(mMonthWheelView.getSelected())) + 1; i++) {
            if (!mShowFutureDate
                    && (Integer.valueOf(mYearWheelView.getSelected()) == mCalendar.get(Calendar.YEAR))
                    && (Integer.valueOf(mMonthWheelView.getSelected()) == mCalendar.get(Calendar.MONTH) + 1)
                    && (i > mCalendar.get(Calendar.DAY_OF_MONTH))) {
                break;
            }
            strList.add("" + i);
        }
        selectedPosition = mDayWheelView.getSelectedPosition();
        mDayWheelView.setData(strList);
        mDayWheelView.setSelectedPosition((selectedPosition < strList.size()) ? selectedPosition : (strList.size() - 1));
    }

    /**
     * 根据指定年月校准日期数据，只改变数据不改变当前选择项
     */
    private void calibrateDayData(int year, int month) {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        int minDay = (
                year == mMinCalendar.get(Calendar.YEAR)
                        && month == mMinCalendar.get(Calendar.MONTH) + 1
        )
                ? mMinCalendar.get(Calendar.DAY_OF_MONTH)
                : 1;
        for (int i = minDay; i < DTPLUtil.getDayNumOfMonth(year, month) + 1; i++) {
            if (!mShowFutureDate
                    && (year == mCalendar.get(Calendar.YEAR))
                    && (month == mCalendar.get(Calendar.MONTH) + 1)
                    && (i > mCalendar.get(Calendar.DAY_OF_MONTH))) {
                break;
            }
            strList.add("" + i);
        }
        mDayWheelView.setData(strList);
    }

    /**
     * 根据年月日变化校准小时数据
     */
    private void calibrateHourData() {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        if ((mYearWheelView.getSelected() == null)
                || (mMonthWheelView.getSelected() == null)
                || (mDayWheelView.getSelected() == null)) {
            return;
        }
        int minHour = (
                Integer.valueOf(mYearWheelView.getSelected()) == mMinCalendar.get(Calendar.YEAR)
                        && Integer.valueOf(mMonthWheelView.getSelected()) == mMinCalendar.get(Calendar.MONTH) + 1
                        && Integer.valueOf(mDayWheelView.getSelected()) == mMinCalendar.get(Calendar.DAY_OF_MONTH)
        )
                ? mMinCalendar.get(Calendar.HOUR_OF_DAY)
                : 0;
        for (int i = minHour; i < 24; i++) {
            if (!mShowFutureDate
                    && (Integer.valueOf(mYearWheelView.getSelected()) == mCalendar.get(Calendar.YEAR))
                    && (Integer.valueOf(mMonthWheelView.getSelected()) == mCalendar.get(Calendar.MONTH) + 1)
                    && (Integer.valueOf(mDayWheelView.getSelected()) == mCalendar.get(Calendar.DAY_OF_MONTH))
                    && (i > mCalendar.get(Calendar.HOUR_OF_DAY))) {
                break;
            }
            strList.add("" + i);
        }
        selectedPosition = mHourWheelView.getSelectedPosition();
        mHourWheelView.setData(strList);
        mHourWheelView.setSelectedPosition((selectedPosition < strList.size()) ? selectedPosition : (strList.size() - 1));
    }

    /**
     * 根据年月日变化校准小时数据，只改变数据不改变当前选择项
     */
    private void calibrateHourData(int year, int month, int day) {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        int minHour = (
                year == mMinCalendar.get(Calendar.YEAR)
                        && month == mMinCalendar.get(Calendar.MONTH) + 1
                        && day == mMinCalendar.get(Calendar.DAY_OF_MONTH)
        )
                ? mMinCalendar.get(Calendar.HOUR_OF_DAY)
                : 0;
        for (int i = minHour; i < 24; i++) {
            if (!mShowFutureDate
                    && (year == mCalendar.get(Calendar.YEAR))
                    && (month == mCalendar.get(Calendar.MONTH) + 1)
                    && (day == mCalendar.get(Calendar.DAY_OF_MONTH))
                    && (i > mCalendar.get(Calendar.HOUR_OF_DAY))) {
                break;
            }
            strList.add("" + i);
        }
        mHourWheelView.setData(strList);
    }

    /**
     * 根据年月日时变化校准分钟数据
     */
    private void calibrateMinuteData() {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        if ((mYearWheelView.getSelected() == null)
                || (mMonthWheelView.getSelected() == null)
                || (mDayWheelView.getSelected() == null)
                || (mHourWheelView.getSelected() == null)) {
            return;
        }
        int minMinute = (
                Integer.valueOf(mYearWheelView.getSelected()) == mMinCalendar.get(Calendar.YEAR)
                        && Integer.valueOf(mMonthWheelView.getSelected()) == mMinCalendar.get(Calendar.MONTH) + 1
                        && Integer.valueOf(mDayWheelView.getSelected()) == mMinCalendar.get(Calendar.DAY_OF_MONTH)
                        && Integer.valueOf(mHourWheelView.getSelected()) == mMinCalendar.get(Calendar.HOUR_OF_DAY)
        )
                ? mMinCalendar.get(Calendar.MINUTE)
                : 0;
        for (int i = minMinute; i < 60; i++) {
            if (!mShowFutureDate
                    && (Integer.valueOf(mYearWheelView.getSelected()) == mCalendar.get(Calendar.YEAR))
                    && (Integer.valueOf(mMonthWheelView.getSelected()) == mCalendar.get(Calendar.MONTH) + 1)
                    && (Integer.valueOf(mDayWheelView.getSelected()) == mCalendar.get(Calendar.DAY_OF_MONTH))
                    && (Integer.valueOf(mHourWheelView.getSelected()) == mCalendar.get(Calendar.HOUR_OF_DAY))
                    && (i > mCalendar.get(Calendar.MINUTE))) {
                break;
            }
            strList.add(((i < 10) ? "0" : "") + i);
        }
        selectedPosition = mMinuteWheelView.getSelectedPosition();
        mMinuteWheelView.setData(strList);
        mMinuteWheelView.setSelectedPosition((selectedPosition < strList.size()) ? selectedPosition : (strList.size() - 1));
    }

    /**
     * 根据年月日时变化校准分钟数据，只改变数据不改变当前选择项
     */
    private void calibrateMinuteData(int year, int month, int day, int hour) {
        int selectedPosition;
        List<String> strList = new ArrayList<>();
        int minMinute = (
                year == mMinCalendar.get(Calendar.YEAR)
                        && month == mMinCalendar.get(Calendar.MONTH) + 1
                        && day == mMinCalendar.get(Calendar.DAY_OF_MONTH)
                        && hour == mMinCalendar.get(Calendar.HOUR_OF_DAY)
        )
                ? mMinCalendar.get(Calendar.MINUTE)
                : 0;
        for (int i = minMinute; i < 60; i++) {
            if (!mShowFutureDate
                    && (year == mCalendar.get(Calendar.YEAR))
                    && (month == mCalendar.get(Calendar.MONTH) + 1)
                    && (day == mCalendar.get(Calendar.DAY_OF_MONTH))
                    && (hour == mCalendar.get(Calendar.HOUR_OF_DAY))
                    && (i > mCalendar.get(Calendar.MINUTE))) {
                break;
            }
            strList.add(((i < 10) ? "0" : "") + i);
        }
        mMinuteWheelView.setData(strList);
    }

    private WheelView.OnItemChangedListener mOnItemChangedListener = new WheelView.OnItemChangedListener() {
        @Override
        public void onItemChanged(WheelView wheelView, int position) {
            if (wheelView.equals(mYearWheelView) || wheelView.equals(mMonthWheelView) || wheelView.equals(mDayWheelView) || wheelView.equals(mHourWheelView)) {
                if (wheelView.equals(mYearWheelView) || wheelView.equals(mMonthWheelView) || wheelView.equals(mDayWheelView)) {
                    if (wheelView.equals(mYearWheelView) || wheelView.equals(mMonthWheelView)) {
                        if (wheelView.equals(mYearWheelView)) {
                            calibrateMonthData();
                        }
                        calibrateDayData();
                    }
                    calibrateHourData();
                }
                calibrateMinuteData();
            }
            if (mOnDateTimeChangedListener != null) {
                mOnDateTimeChangedListener.onChanged(getYear(), getMonth(), getDay(), getHour(), getMinute());
            }
        }
    };

    public void setOnDateTimeChangedListener(OnDateTimeChangedListener onDateTimeChangedListener) {
        mOnDateTimeChangedListener = onDateTimeChangedListener;
    }

    /**
     * 当日期或时间发生改变时的回调
     */
    public interface OnDateTimeChangedListener {
        void onChanged(int year, int month, int day, int hour, int minute);
    }

}
